/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.elcom.emocbc.cbcgateway.ipc;

import com.elcom.emocbc.cbcgateway.httpclient.CbcHttpClient;
import com.elcom.emocbc.cbcgateway.httpclient.xml.BroadcastAck;
import com.elcom.emocbc.cbgw.message.BroadcastSubmitRequest;
import com.elcom.emocbc.cbgw.message.metadata.XmlApiException;
import com.elcom.ipc.synchronize.message.IPC_REQUEST;
import com.elcom.emocbc.common.ipc.message.cbcmessage.BROADCAST_SUBMIT_ACK;
import com.elcom.emocbc.common.ipc.message.cbcmessage.BROADCAST_SUBMIT_ACK.ResponseStatus;
import com.elcom.emocbc.common.ipc.message.cbcmessage.BROADCAST_SUBMIT_REQ;
import com.elcom.util.Constant;
import java.io.IOException;
import org.apache.http.client.ClientProtocolException;
import org.jconfig.Configuration;
import org.jconfig.ConfigurationManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author GiangLT
 */
public class HttpForwardTask implements Runnable {

    private IPC_REQUEST request;
    private final Logger logger = LoggerFactory.getLogger(HttpForwardTask.class);
    private int retryNum = 0;
    private int maxRetryNum;
    private long retryIntevalMs;
    private Exception mainException;

    public HttpForwardTask(IPC_REQUEST request) {
        this.request = request;
        Configuration config = ConfigurationManager.getConfiguration(Constant.SYS_CONFIG_NAME);
        maxRetryNum = config.getIntProperty("maxRetryNum", 3, "httpConnection");
        retryIntevalMs = config.getLongProperty("retryIntevalMs", 5000, "httpConnection");
    }

    @Override
    public void run() {
        if (request instanceof BROADCAST_SUBMIT_REQ) {
            BROADCAST_SUBMIT_REQ ipcRequest = (BROADCAST_SUBMIT_REQ) request;
            logger.info("[Zone: {}] <<< Received {}", ipcRequest.getBroadcastAreaName(), ipcRequest);
            processIpcRequest(ipcRequest);
        } else {
            logger.info("<<< Received unsupported msg: {}", request);
        }
    }

    private void processIpcRequest(BROADCAST_SUBMIT_REQ ipcRequest) {
        String zone = ipcRequest.getBroadcastAreaName();
        if (retryNum <= maxRetryNum) {

            try {
                BroadcastSubmitRequest broadcastSubmitRequest = new BroadcastSubmitRequest();
                broadcastSubmitRequest.setBroadcastText(ipcRequest.getBroadcastText());
                broadcastSubmitRequest.setBroadcastAreaName(ipcRequest.getBroadcastAreaName());

                //<editor-fold defaultstate="collapsed" desc="optional values">
                //                broadcastSubmitRequest.setBoundaryLongtitude(ipcRequest.getBoundaryLongtitude());
                //                broadcastSubmitRequest.setBoundayLatitude(ipcRequest.getBoundayLatitude());
                //                broadcastSubmitRequest.setBroadcastAreaShape(ipcRequest.getBroadcastAreaShape());
                //                broadcastSubmitRequest.setBroadcastChannelNumber(ipcRequest.getBroadcastChannelNumber());
                //                broadcastSubmitRequest.setBroadcastChannelType(BROADCAST_SUBMIT_REQ.BroadcastChannelType.enumValueOf(ipcRequest.getBroadcastChannelType()).name());
                //                broadcastSubmitRequest.setCentroidLatitude(ipcRequest.getCentroidLatitude());
                //                broadcastSubmitRequest.setCentroidLongtitude(ipcRequest.getCentroidLongtitude());
                //                broadcastSubmitRequest.setConfirmationInd(ipcRequest.getConfirmationInd());
                //                broadcastSubmitRequest.setDataCodingScheme(ipcRequest.getConfirmationInd());
                //                broadcastSubmitRequest.setGeographicalScope(null);
                //                broadcastSubmitRequest.setLanguage(null);
                //                broadcastSubmitRequest.setLatitude(null);
                //                broadcastSubmitRequest.setLongitude(null);
                //                broadcastSubmitRequest.setMsgPriority(null);
                //                broadcastSubmitRequest.setNumOfBroadcast(null);
                //                broadcastSubmitRequest.setRadius(null);
                //                broadcastSubmitRequest.setRepetitionPeriod(null);
                //                broadcastSubmitRequest.setShapeName(null);
                //                broadcastSubmitRequest.setStartBroadcastLatitude(null);
                //                broadcastSubmitRequest.setStartBroadcastLongitude(null);
                //                broadcastSubmitRequest.setStartTime(null);
                //                broadcastSubmitRequest.setStopTime(null);
                //                broadcastSubmitRequest.setXLongRadius(null);
                //                broadcastSubmitRequest.setXRadius(null);
                //                broadcastSubmitRequest.setYLatRadius(null);
                //                broadcastSubmitRequest.setYRadius(null);
                //</editor-fold>

                BroadcastAck ack = CbcHttpClient.getInstance().send(broadcastSubmitRequest);
                String status = ack.getAckMessageType().getStatus().getVal();
                logger.trace("Status: " + status);

                BROADCAST_SUBMIT_ACK ipcResponse = (BROADCAST_SUBMIT_ACK) ipcRequest.getResponse();
                ipcResponse.setCbcTransactionId(ack.getAckMessageType().getCBCTransactionID());
                if (status.equalsIgnoreCase(ResponseStatus.COMPLETED.toString())) {
                    ipcResponse.setErrorCode(0);
                    ipcResponse.setErrorDetail(status + " | Successfully broadcasted to BSC");
                    ipcResponse.setResponseStatus("COMPLETED");
                    ipcResponse.setFailureReason("COMPLETED");
                }
                //<editor-fold defaultstate="collapsed" desc="Other unused reponses">
                //                else if (status.equals(ResponseStatus.PARTIAL_COMPLETED.toString())) {
                //                    ipcResponse.setErrorCode(BROADCAST_SUBMIT_ACK.BROADCAST_RESULT.PARTIAL_COMPLETE.value());
                //                    String failureReason = ack.getAckMessageType().getFailureReason();
                //                    ipcResponse.setErrorDetail(status + " | Partialy broadcasted to BSC | " + failureReason);
                //                    ipcResponse.setResponseStatus(status);
                //                    ipcResponse.setFailureReason(failureReason);
                //                } else {
                //                    ipcResponse.setErrorCode(BROADCAST_SUBMIT_ACK.BROADCAST_RESULT.FAILED.value());
                //                    String failureReason = ack.getAckMessageType().getFailureReason();
                //                    ipcResponse.setErrorDetail(status + " | Failed to broadcast to BSC | " + failureReason);
                //                    ipcResponse.setResponseStatus(status);
                //                    ipcResponse.setFailureReason(failureReason);
                //                }
                //</editor-fold>

                logger.trace("[Zone: {}] >>> Sending IPC response: {}", zone, ipcResponse);
                ipcRequest.sendResponse(ipcResponse);
                logger.info("[Zone: {}] >>> Sent IPC response to: {}", zone, ipcResponse);

            } catch (Exception ex) {
                logger.error("[Zone: {}] Error while processing request. Retry #{}", new Object[]{zone, retryNum, ex});
                mainException = ex;

                retryNum++;
                try {
                    Thread.sleep(retryIntevalMs);
                } catch (InterruptedException ex1) {
                    logger.error("", ex1);
                }

                if (reconnect()) {
                    processIpcRequest(ipcRequest);
                }
            }
        } else {
            BROADCAST_SUBMIT_ACK ipcResponse = (BROADCAST_SUBMIT_ACK) ipcRequest.getResponse();
            if (mainException instanceof XmlApiException) {
                XmlApiException ex = (XmlApiException) mainException;
                logger.error("[Zone: {}] CBC return an error message: ", zone, ex);

                String failureReason = ex.getFailureReason();
                ipcResponse.setCbcTransactionId("");
                ipcResponse.setErrorCode(BROADCAST_SUBMIT_ACK.BROADCAST_RESULT.FAILED.value());
                ipcResponse.setErrorDetail("CBC return an error message: " + ex.getMessage());
                ipcResponse.setResponseStatus(ex.getStatus());
                ipcResponse.setFailureReason(failureReason);
            } else if (mainException instanceof ClientProtocolException) {
                logger.error("[Zone: {}] HTTP protocol error ", zone, mainException);
                ipcResponse.setErrorDetail("HTTP protocol error: " + mainException.getMessage());
                ipcResponse.setResponseStatus("FAILED");
                ipcResponse.setFailureReason(mainException.getMessage());
            } else if (mainException instanceof IOException) {
                logger.error("[Zone: {}] Network IO exception ", zone, mainException);
                ipcResponse.setCbcTransactionId("");
                ipcResponse.setErrorCode(BROADCAST_SUBMIT_ACK.BROADCAST_RESULT.UNKNOWN.value());
                ipcResponse.setErrorDetail("Network IO exception: " + mainException.getMessage());
                ipcResponse.setResponseStatus("FAILED");
                ipcResponse.setFailureReason(mainException.getMessage());
            } else {
                logger.error("[Zone: {}] Error while handling message ", zone, mainException);
                ipcResponse.setCbcTransactionId("");
                ipcResponse.setErrorCode(BROADCAST_SUBMIT_ACK.BROADCAST_RESULT.UNKNOWN.value());
                ipcResponse.setErrorDetail("Error while handling message: " + mainException.getMessage());
                ipcResponse.setResponseStatus("FAILED");
                ipcResponse.setFailureReason(mainException.getMessage());
            }
            logger.trace("[Zone: {}] >>> Sending response: {}", zone, ipcResponse);
            ipcRequest.sendResponse(ipcResponse);
            logger.info("[Zone: {}] >>> Sent response: {}", zone, ipcResponse);
        }
    }

    private boolean reconnect() {
        if (retryNum <= maxRetryNum) {
            try {
                logger.warn("Reconnecting to CBC server... ");
                CbcHttpClient cbcHttpClient = CbcHttpClient.getInstance();
                cbcHttpClient.shutdownConnection();
                cbcHttpClient.connect();
                cbcHttpClient.forceUnbind();
                cbcHttpClient.bind();
                logger.warn("Reconnected to CBC server");

                return true;
            } catch (Exception ex) {
                logger.error("Unable to reconnect to CBC server", ex);
                retryNum++;
                try {
                    Thread.sleep(retryIntevalMs);
                } catch (InterruptedException ex1) {
                    logger.error("", ex1);
                }
                reconnect();
            }
        }
        return false;
    }

    public IPC_REQUEST getRequest() {
        return request;
    }
}
